Expressで作るアプリ開発入門 追加実装編

Express

前回の内容

前回は、一覧ページの実装をしました↓
https://sakublog.tech/handson/express-app-2

今回は商品を追加できるようにします。

body-parserのインストール

ejsからフォームで送信した値をExpress側で受け取るには
body-parserというライブラリ
https://www.npmjs.com/package/body-parser
を使用します。

npm i body-parser

app.jsに追記します。

import express from "express";
import mysql from "mysql2/promise";
import bodyParser from "body-parser"; // 追記
import "dotenv/config";
const app = express();
const port = 3000;

app.set("view engine", "ejs");
app.use(express.static("public"));

app.use(bodyParser.urlencoded({ extended: false }));  // 追記


これで、Expressでフォームからのデータを受け取れるようになります。

extendedは、データをエンコードするかのオプションで、今回は特に必要ないのでfalseにしています。

ExpressでPOST送信を受け取る

まずはadd.ejsのフォーム送信のactionを決めます。
今回は/addに送信します。

<form action="/add" method="POST">

こちらをapp.jsに追記します↓

app.post("/add", (req, res) => {
  console.log(req.body);
  res.redirect("/");
});

フォームの送信データは、req.bodyで受け取ることができます。
その後、リダイレクトさせて一覧に遷移しています。

実際に画面から確認してみましょう。

ターミナルを確認すると、フォームで送信した値が受け取れています↓

バリデーション

不正な値ではないか確認しましょう。
今回は下記の通りにバリデーションを行います。

  • 商品名
    • 20文字以内
  • 価格
    • 1~999までの整数
  • あったかいorつめたい
    • 1か0

商品名のバリデーション

app.jsを次のように編集します。

app.post("/add", (req, res) => {
  const errors = {
    name: [],
    price: [],
    temp: [],
  };
  if (req.body.name.length > 20) {
    errors.name.push("商品名は20文字以内です。");
  }
  if (errors.name.length) {
    return res.render("add", { errors: errors });
  }
  res.redirect("/");
});

errorsにエラーの状態を管理します。
バリデーションエラーがあれば、errorsに追加します。

商品名はreq.body.name
で受け取れるので、20文字以内かをlengthで判定しています。

エラーがあった場合(error.name.lengthが1以上)、add.ejsにerrorsを渡してレンダリングします。
次に、add.ejsで、errorsを受けとってエラーの内容を表示します。

        <label for="drink-name">商品名:</label>
        <input type="text" id="drink-name" name="name" required />
        <!-- 追加 -->
        <p style="color: red"><%= errors.name.length ? errors.name[0] :"" %></p>

nameにエラーがあれば、表示させます。

これだと/addにアクセスしたときにエラーになるので、app.get(“/add”)
の方にもerrorsのオブジェクトを渡します。

app.get("/add", (req, res) => {
  const errors = {
    name: [],
    price: [],
    temp: [],
  };
  res.render("add", { errors: errors });
});

これで、商品名を20文字以上入れて追加すると、、、


このように、バリデーションで弾くことができました!
もっと使いやすくするには、バリデーション前のデータをフォームに入力された状態でエラーを表示するのが良いです。
今回は簡略化のため、なしとします。

価格のバリデーション

app.jsに追記していきます。

  if (req.body.price >= 1000 || req.body.price <= 0) {
    errors.price.push("価格は999円以内で設定できます。");
  }
  if (errors.name.length || errors.price.length) {
    return res.render("add", { errors: errors });
  }

res.render

のif文にも、priceを条件に追加します。

add.ejsの価格の箇所を変更します。商品名とほぼ同じ内容です。

        <label for="drink-price">価格:</label>
        <input type="number" id="drink-price" name="price" required />
        <p style="color: red">
          <%= errors.price.length ? errors.price[0] :"" %>
        </p>

これで、値段を1000円以上か0以下で入力します。

すると、価格のところでエラーになりました!

これで価格のバリデーションができました。

温度のバリデーション

温度は、フロント側で不正な値に変えて確認するのはやや面倒です。

今回はコードだけ変更をします。

といっても、やることは商品名や価格と同じです。

app.jsに追記します。

tempの数字は文字列で渡ってくることに注意です。

  if (req.body.temp !== "1" && req.body.temp !== "0") {
    errors.temp.push("温度の選択肢が不正です。");
  }
  if (errors.name.length || errors.price.length || errors.temp.length) {
    return res.render("add", { errors: errors });
  }

add.ejs↓

        <label for="drink-temp">温度:</label>
        <select id="drink-temp" name="temp">
          <option value="1">あたたかい</option>
          <option value="0">冷たい</option>
        </select>
        <p style="color: red"><%= errors.temp.length ? errors.temp[0] :"" %></p>

これでOKです!

最後に、20や1000などのマジックナンバーは定数に移行しましょう。

app.jsに、3つの定数を追記します。

const port = 3000;

// 定数を追加
const MAX_NAME_LENGTH = 20;
const MAX_PRICE = 1000;
const MIN_PRICE = 0;
const TEMP_WARM = "1";
const TEMP_COLD = "0";

app.set("view engine", "ejs");

これらを呼び出すようにします↓

app.post("/add", (req, res) => {
  const errors = {
    name: [],
    price: [],
    temp: [],
  };
  if (req.body.name.length > MAX_NAME_LENGTH) {
    errors.name.push("商品名は20文字以内です。");
  }
  if (req.body.price >= MAX_PRICE || req.body.price <= MIN_PRICE) {
    errors.price.push("価格は999円以内で設定できます。");
  }
  if (req.body.temp !== TEMP_WARM && req.body.temp !== TEMP_COLD) {
    errors.temp.push("温度の選択肢が不正です。");
  }
  if (errors.name.length || errors.price.length || errors.temp.length) {
    return res.render("add", { errors: errors });
  }
  res.redirect("/");
});

これでバリデーションは完成です!

データベースに追加

バリデーションをパスした値を、データベースに追加します。

app.jsにINSERTする処理を追加します。

  if (errors.name.length || errors.price.length || errors.temp.length) {
    return res.render("add", { errors: errors });
  }
 // 追加
  const connection = await mysql.createConnection({
    host: process.env.MYSQL_HOST,
    database: process.env.MYSQL_DATABASE,
    user: process.env.MYSQL_USER,
    password: process.env.MYSQL_PASSWORD,
  });
  const sql = `INSERT INTO drinks (name,price,temperature) VALUES(?,?,?)`;
  await connection.query(sql, [req.body.name, req.body.price, req.body.temp]);
  await connection.end();
  res.redirect("/");

queryメソッドで追加できます。

INSERT | Quickstart
The examples below also work for the execute method.

ユーザーからの値をSQLで使用するので、

プリペアドステートメントを使用することに注意してください。

(知らない方はプリペアドステートメントで検索すると出てきます)

ユーザーからの値を「?」にし、queryメソッドの第二引数の配列で順番に並べればOKです。

これで、追加できるか確認してみます。

追加されていれば完成です!

お疲れ様でした。

次回は更新機能を作成していきます!!!